#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "../../Include/manager/matrix_manager.h"
#include "../../Include/manager/config_manager.h"
#include "../../Include/manager/stock_transaction_data_manager.h"
#include "../../Include/manager/function_manager.h"
#include "../../Include/domain/bp_info.h"
#include "../../Include/domain/stock_transaction_data_list_result.h"
#include "../../Include/manager/log_manager.h"
#include "../../Include/util/array_util.h"

/**
 * 执行反向传播算法
 */
void DoBP() {
    Info("开始执行反向传播算法");

    /*********************************** 获取反向传播算法的信息 ***********************************/
    struct BPInfo *bpInfo = GetBPInfo();

    /************************ 生成两个权重矩阵w1、w2，每个元素值都是随机生成的 ***********************/
    MATRIX_COLUMN = bpInfo->w1ColumnNumber;
    double (*w1)[bpInfo->w1ColumnNumber] = (double *) malloc(
            sizeof(double) * bpInfo->w1RowNumber * bpInfo->w1ColumnNumber);
    CreateMatrix(bpInfo->arrayRandomLimit, bpInfo->w1RowNumber,
                 bpInfo->w1ColumnNumber, w1);
    // 注意：下面这句必须要，这是给bpInfo重新赋值，否则w2RowNumber和w2ColumnNumber的值会出错
//    bpInfo = GetBPInfo();
    MATRIX_COLUMN = bpInfo->w2ColumnNumber;
    double (*w2)[bpInfo->w1ColumnNumber] = (double *) malloc(
            sizeof(double) * bpInfo->w2RowNumber * bpInfo->w2ColumnNumber);
    CreateMatrix(bpInfo->arrayRandomLimit, bpInfo->w2RowNumber,
                 bpInfo->w2ColumnNumber, w2);

    /************************************ 获取x矩阵（训练数据） **********************************/
    // 注意：下面这句必须要，这是给bpInfo重新赋值，否则w2RowNumber和w2ColumnNumber的值会出错
//    bpInfo = GetBPInfo();
    struct StockTransactionDataListResult stockTransactionDataListResult = FindStockTransactionData(bpInfo->trainDataSetBeginDate, bpInfo->trainDataSetEndDate);
    CHARACTERISTIC_VALUE_NUMBER = bpInfo->w1RowNumber;
    double (*x)[CHARACTERISTIC_VALUE_NUMBER] = StockTransactionDataListToArray(
            stockTransactionDataListResult.trainDataList, stockTransactionDataListResult.count);

    /********************************** 生成偏置项矩阵bias1、bias2 *******************************/
    MATRIX_COLUMN = bpInfo->w1ColumnNumber;
    double (*bias1)[bpInfo->w1ColumnNumber] = (double *) malloc(
            sizeof(double) * stockTransactionDataListResult.count * bpInfo->w1ColumnNumber);
    CreateMatrix(bpInfo->arrayRandomLimit, stockTransactionDataListResult.count,
                 bpInfo->w1ColumnNumber, bias1);
    MATRIX_COLUMN = bpInfo->w2ColumnNumber;
    double (*bias2)[bpInfo->w2ColumnNumber] = (double *) malloc(
            sizeof(double) * stockTransactionDataListResult.count * bpInfo->w2ColumnNumber);
    CreateMatrix(bpInfo->arrayRandomLimit, stockTransactionDataListResult.count,
                 bpInfo->w2ColumnNumber, bias2);

    /********* 计算a矩阵。x矩阵乘以w1矩阵，加上bias1偏置项，再调用ReLU函数（线性变为非线性） ***********/
    // 计算a矩阵。x矩阵乘以w1矩阵，加上bias1偏置项，再调用ReLU函数（线性变为非线性）
    double (*_a)[bpInfo->w1ColumnNumber] = (double *) malloc(
            sizeof(double) * stockTransactionDataListResult.count * bpInfo->w1ColumnNumber);
    // 下面这行代码需要重置一下a，否则在671行20列的数据会出错
    x = StockTransactionDataListToArray(stockTransactionDataListResult.trainDataList,
                                        stockTransactionDataListResult.count);
    A_COLUMN = CHARACTERISTIC_VALUE_NUMBER;
    B_COLUMN = bpInfo->w1ColumnNumber;
    A_ROW = stockTransactionDataListResult.count;
    Multiply(x, w1, _a);
    double (*a_)[bpInfo->w1ColumnNumber] = (double *) malloc(
            sizeof(double) * stockTransactionDataListResult.count * bpInfo->w1ColumnNumber);
    A_COLUMN = bpInfo->w1ColumnNumber;
    Add(_a, bias1, a_);
    RELU_ARRAY_ROW = stockTransactionDataListResult.count;
    RELU_ARRAY_COLUMN = bpInfo->w1ColumnNumber;
    double (*a)[bpInfo->w1ColumnNumber] = ReLUArray(a_);

    /********* 计算y矩阵。a矩阵乘以w2矩阵，加上bias2偏置项，再调用ReLU函数（线性变为非线性） ***********/
    double (*_y)[bpInfo->w2ColumnNumber] = (double *) malloc(
            sizeof(double) * stockTransactionDataListResult.count * bpInfo->w2ColumnNumber);
    A_COLUMN = bpInfo->w1ColumnNumber;
    B_COLUMN = bpInfo->w2ColumnNumber;
    A_ROW = stockTransactionDataListResult.count;
    Multiply(a, w2, _y);
    double (*y_)[bpInfo->w2ColumnNumber] = (double *) malloc(
            sizeof(double) * stockTransactionDataListResult.count * bpInfo->w2ColumnNumber);
    A_COLUMN = bpInfo->w2ColumnNumber;
    Add(_y, bias1, y_);
    RELU_ARRAY_ROW = stockTransactionDataListResult.count;
    RELU_ARRAY_COLUMN = bpInfo->w2ColumnNumber;
    double (*y)[bpInfo->w2ColumnNumber] = ReLUArray(y_);

    /************************************ 使用反向神经网络算法 **********************************/
    // 计算模型的参数数组w1w2
    double (*w1w2)[bpInfo->w2ColumnNumber] = (double *) malloc(
            sizeof(double) * stockTransactionDataListResult.count * bpInfo->w2ColumnNumber);
    A_COLUMN = bpInfo->w1ColumnNumber;
    B_COLUMN = bpInfo->w2ColumnNumber;
    A_ROW = bpInfo->w1RowNumber;
    Multiply(w1, w2, w1w2);
    for (int i = 0; i < bpInfo->w1RowNumber; ++i) {
        printf("%f\t", *(*(w1w2 + i) + 0));
    }
    printf("\n");
    // 求损失值。通过求均方误差MSE的方法求损失值
    double mean = CalculateLossValue(stockTransactionDataListResult.count, bpInfo->w2ColumnNumber, stockTransactionDataListResult.trainLabelList, y);
    // l2正则化（解决过拟合问题）
    mean = mean + L2Regularization(w1w2, bpInfo->w1RowNumber, bpInfo->l2RegularizationRate);
    InfoDouble("损失值为", mean);
    // 反向神经网络算法，梯度下降算法
    int i = 1;
    // 初始化学习率
    double learningRate = bpInfo->initLearningRate;
    while (bpInfo->lossValue < mean) {
        // 使用指数衰减法，计算学习率
        learningRate = CalculateLearningRateByExponentialDecay(learningRate, bpInfo->decayRate, GLOBAL_STEP, bpInfo->decaySteps);
        // 每次迭代后都要加一
        GLOBAL_STEP++;
        // 通过梯度下降算法，计算新的参数列表
        w1w2 = CalculateNewParameterByGradientDescentAlgorithm(w1w2, bpInfo->w1RowNumber, learningRate);
        for (int i = 0; i < bpInfo->w1RowNumber; ++i) {
            printf("%f\t", *(*(w1w2 + i) + 0));
        }
        printf("\n");
        // 计算测试结果
        y = (double *) malloc(sizeof(double) * stockTransactionDataListResult.count * bpInfo->w2ColumnNumber);
        Multiply(x, w1w2, y);
        // 求损失值。通过求均方误差MSE的方法求损失值
        mean = CalculateLossValue(stockTransactionDataListResult.count, bpInfo->w2ColumnNumber,
                                  stockTransactionDataListResult.trainLabelList, y);
        // l2正则化（解决过拟合问题）
        mean = mean + L2Regularization(w1w2, bpInfo->w1RowNumber, bpInfo->l2RegularizationRate);
        InfoDouble("损失值为", mean);
        i++;
    }
    InfoInt("迭代次数", i);

    /******************************* 打印模型的结果，和真是的收盘价作比较 ***************************/
    stockTransactionDataListResult = FindStockTransactionData(bpInfo->testDataSetBeginDate, bpInfo->testDataSetEndDate);
    CHARACTERISTIC_VALUE_NUMBER = bpInfo->w1RowNumber;
    x = StockTransactionDataListToArray(stockTransactionDataListResult.trainDataList, stockTransactionDataListResult.count);
    A_COLUMN = bpInfo->w1RowNumber;
    B_COLUMN = bpInfo->w2ColumnNumber;
    A_ROW = stockTransactionDataListResult.count;
    Multiply(x, w1w2, y);
    for (int i = 0; i < bpInfo->w1RowNumber; ++i) {
        printf("%f\t%f\n", *(*(stockTransactionDataListResult.trainLabelList + i) + 0), *(*(y + i) + 0));
    }

    Info("反向传播算法执行完毕");
}
